<HTML>
 
<!-- Mirrored from www.javapractices.com/apps/movies/javadoc/src-html/hirondelle/movies/edit/Movie.html by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:30:20 GMT -->
<HEAD>
  <TITLE>
Movie.java
  </TITLE>
  <LINK REL ='stylesheet' TYPE='text/css' HREF='../../../../highlight.css' TITLE='Style'>
 </HEAD>
 <BODY>
<PRE>
<span class='keyword'>package</span> hirondelle.movies.edit;<a name=line.1></a>
<a name=line.2></a>
<span class='keyword'>import</span> java.math.BigDecimal;<a name=line.3></a>
<span class='keyword'>import</span> java.util.*;<a name=line.4></a>
<a name=line.5></a>
<span class='keyword'>import</span> hirondelle.movies.exception.InvalidInputException;<a name=line.6></a>
<span class='keyword'>import</span> hirondelle.movies.util.Util;<a name=line.7></a>
<a name=line.8></a>
<span class='comment'>/**<a name=line.9></a>
 Data-centric class encapsulating all fields related to movies. <a name=line.10></a>
 <a name=line.11></a>
 &lt;P&gt;This class exists  in order to encapsulate, validate, and sort movie information.<a name=line.12></a>
  This class is used both to validate user input, and act as a 'transfer object' when <a name=line.13></a>
  interacting with the database.<a name=line.14></a>
 <a name=line.15></a>
  &lt;P&gt;&lt;b&gt;This class would greatly benefit from a JUnit test class, to test its data <a name=line.16></a>
  validation and sorting.&lt;/b&gt;<a name=line.17></a>
*/</span><a name=line.18></a>
<span class='keyword'>final</span> <span class='keyword'>class</span> Movie <span class='keyword'>implements</span> Comparable&lt;Movie&gt;{<a name=line.19></a>
<a name=line.20></a>
  <span class='comment'>/**<a name=line.21></a>
   Constructor taking regular Java objects natural to the domain.<a name=line.22></a>
   <a name=line.23></a>
   &lt;P&gt;When the user has entered text, this constructor is called indirectly, through <a name=line.24></a>
   {@link #Movie(String, String, String, String, String)}.<a name=line.25></a>
   <a name=line.26></a>
   @param aId optional, the database identifer for the movie. This item is optional since, <a name=line.27></a>
   for 'add' operations,  it has yet to be assigned by the database.<a name=line.28></a>
   @param aTitle has content, name of the movie<a name=line.29></a>
   @param aDateViewed optional, date the movie was screened by the user<a name=line.30></a>
   @param aRating optional, in range 0.0 to 10.0<a name=line.31></a>
   @param aComment optional, any comment on the movie<a name=line.32></a>
  */</span><a name=line.33></a>
  Movie(<a name=line.34></a>
    String aId, String aTitle, Date aDateViewed, BigDecimal aRating, String aComment<a name=line.35></a>
  ) <span class='keyword'>throws</span> InvalidInputException {<a name=line.36></a>
    fId = aId;<a name=line.37></a>
    fTitle = aTitle;<a name=line.38></a>
    fDateViewed = aDateViewed;<a name=line.39></a>
    fRating = aRating;<a name=line.40></a>
    fComment = aComment;<a name=line.41></a>
    validateState();<a name=line.42></a>
  }<a name=line.43></a>
  <a name=line.44></a>
  <span class='comment'>/**<a name=line.45></a>
   Constructor which takes all parameters as &lt;em&gt;text&lt;/em&gt;.<a name=line.46></a>
   <a name=line.47></a>
   &lt;P&gt;Raw user input is usually in the form of &lt;em&gt;text&lt;/em&gt;.<a name=line.48></a>
   This constructor &lt;em&gt;first&lt;/em&gt; parses such text into the required 'base objects' - <a name=line.49></a>
   {@link Date}, {@link BigDecimal} and so on. If those parse operations &lt;em&gt;fail&lt;/em&gt;, <a name=line.50></a>
   then the user is shown an error message. If N such errors are present in user input, <a name=line.51></a>
   then  N &lt;em&gt;separate&lt;/em&gt; message will be presented for each failure, one by one.<a name=line.52></a>
   <a name=line.53></a>
   &lt;P&gt;If all such parse operations &lt;em&gt;succeed&lt;/em&gt;, then the "regular" constructor <a name=line.54></a>
   {@link #Movie(String, String, Date, BigDecimal, String)}<a name=line.55></a>
   will then be called. It's important to note that this call to the second constructor <a name=line.56></a>
   can in turn result in &lt;em&gt;another&lt;/em&gt; error message being shown to the <a name=line.57></a>
   user (just one this time). <a name=line.58></a>
 */</span><a name=line.59></a>
  Movie(<a name=line.60></a>
    String aId, String aTitle, String aDateViewed, String aRating, String aComment<a name=line.61></a>
  ) <span class='keyword'>throws</span> InvalidInputException {<a name=line.62></a>
      <span class='keyword'>this</span>(<a name=line.63></a>
        aId, aTitle, Util.parseDate(aDateViewed, <span class='literal'>"Date Viewed"</span>), <a name=line.64></a>
        Util.parseBigDecimal(aRating, <span class='literal'>"Rating"</span>), aComment<a name=line.65></a>
      );<a name=line.66></a>
  }<a name=line.67></a>
  <a name=line.68></a>
  String getId(){ <span class='keyword'>return</span> fId; }<a name=line.69></a>
  <a name=line.70></a>
  <span class='comment'>/**<a name=line.71></a>
   This set method is rather artificial. It results from the toy persistence layer. <a name=line.72></a>
   It's dissatisfying to add this method since the class would otherwise be immutable. <a name=line.73></a>
   Immutability is a highly desirable characteristic.<a name=line.74></a>
  */</span><a name=line.75></a>
  <span class='keyword'>void</span> setId(String aId){  fId = aId; }<a name=line.76></a>
  <a name=line.77></a>
  String getTitle(){ <span class='keyword'>return</span> fTitle; }<a name=line.78></a>
  Date getDateViewed(){ <span class='keyword'>return</span> fDateViewed; }<a name=line.79></a>
  BigDecimal getRating(){ <span class='keyword'>return</span> fRating; }<a name=line.80></a>
  String getComment(){ <span class='keyword'>return</span> fComment; }<a name=line.81></a>
  <a name=line.82></a>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> <span class='keyword'>boolean</span> equals(Object aThat){<a name=line.83></a>
    <span class='keyword'>if</span> ( <span class='keyword'>this</span> == aThat ) <span class='keyword'>return</span> <span class='keyword'>true</span>;<a name=line.84></a>
    <span class='keyword'>if</span> ( !(aThat <span class='keyword'>instanceof</span> Movie) ) <span class='keyword'>return</span> <span class='keyword'>false</span>;<a name=line.85></a>
    Movie that = (Movie)aThat;<a name=line.86></a>
    <span class='keyword'>return</span> <a name=line.87></a>
      areEqual(<span class='keyword'>this</span>.fTitle, that.fTitle) &amp;&amp; <a name=line.88></a>
      areEqual(<span class='keyword'>this</span>.fDateViewed, that.fDateViewed) &amp;&amp; <a name=line.89></a>
      areEqual(<span class='keyword'>this</span>.fRating, that.fRating) &amp;&amp; <a name=line.90></a>
      areEqual(<span class='keyword'>this</span>.fComment, that.fComment)<a name=line.91></a>
    ; <a name=line.92></a>
  }<a name=line.93></a>
  <a name=line.94></a>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> <span class='keyword'>int</span> hashCode(){<a name=line.95></a>
    <span class='keyword'>int</span> result = <span class='literal'>17</span>;<a name=line.96></a>
    result = addHash(result, fTitle);<a name=line.97></a>
    result = addHash(result, fDateViewed);<a name=line.98></a>
    result = addHash(result, fRating);<a name=line.99></a>
    result = addHash(result, fComment);<a name=line.100></a>
    <span class='keyword'>return</span> result;<a name=line.101></a>
  }<a name=line.102></a>
  <a name=line.103></a>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> String toString(){<a name=line.104></a>
    <span class='keyword'>return</span> <a name=line.105></a>
      <span class='literal'>"Movie  Id:"</span> + fId + <span class='literal'>" Title:"</span> + fTitle + <span class='literal'>" Date Viewed:"</span> + fDateViewed + <a name=line.106></a>
      <span class='literal'>" Rating:"</span> + fRating + <span class='literal'>" Comment: "</span> + fComment<a name=line.107></a>
    ; <a name=line.108></a>
  }<a name=line.109></a>
  <a name=line.110></a>
  <span class='comment'>/** Default sort by Date Viewed, then Title. Dates have the most recent items listed first. */</span><a name=line.111></a>
  <span class='keyword'>public</span> <span class='keyword'>int</span> compareTo(Movie aThat) {<a name=line.112></a>
    <span class='keyword'>if</span> ( <span class='keyword'>this</span> == aThat ) <span class='keyword'>return</span> EQUAL;<a name=line.113></a>
   <a name=line.114></a>
    <span class='keyword'>int</span> comparison = DESCENDING * comparePossiblyNull(<span class='keyword'>this</span>.fDateViewed, aThat.fDateViewed);<a name=line.115></a>
    <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.116></a>
    <a name=line.117></a>
    comparison = <span class='keyword'>this</span>.fTitle.compareTo(aThat.fTitle);<a name=line.118></a>
    <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.119></a>
    <a name=line.120></a>
    comparison = comparePossiblyNull(<span class='keyword'>this</span>.fRating, aThat.fRating);<a name=line.121></a>
    <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.122></a>
   <a name=line.123></a>
    comparison = comparePossiblyNull(<span class='keyword'>this</span>.fComment, aThat.fComment);<a name=line.124></a>
    <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.125></a>
    <a name=line.126></a>
    <span class='keyword'>return</span> EQUAL;<a name=line.127></a>
  }<a name=line.128></a>
  <a name=line.129></a>
  <span class='comment'>/** Sort by Title. */</span><a name=line.130></a>
  <span class='keyword'>static</span> Comparator&lt;Movie&gt; TITLE_SORT = <span class='keyword'>new</span> Comparator&lt;Movie&gt;(){<a name=line.131></a>
    <span class='keyword'>public</span> <span class='keyword'>int</span> compare(Movie aThis, Movie aThat) {<a name=line.132></a>
      <span class='keyword'>if</span> ( aThis == aThat ) <span class='keyword'>return</span> EQUAL;<a name=line.133></a>
<a name=line.134></a>
      <span class='keyword'>int</span> comparison = aThis.fTitle.compareTo(aThat.fTitle);<a name=line.135></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.136></a>
      <a name=line.137></a>
      comparison = DESCENDING * comparePossiblyNull(aThis.fDateViewed, aThat.fDateViewed);<a name=line.138></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.139></a>
      <a name=line.140></a>
      comparison = comparePossiblyNull(aThis.fRating, aThat.fRating);<a name=line.141></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.142></a>
     <a name=line.143></a>
      comparison = comparePossiblyNull(aThis.fComment, aThat.fComment);<a name=line.144></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.145></a>
      <a name=line.146></a>
      <span class='keyword'>return</span> EQUAL;<a name=line.147></a>
    };<a name=line.148></a>
  };<a name=line.149></a>
  <a name=line.150></a>
  <span class='comment'>/** Sort by Rating (descending), then Date Viewed (descending). */</span><a name=line.151></a>
  <span class='keyword'>static</span> Comparator&lt;Movie&gt; RATING_SORT = <span class='keyword'>new</span> Comparator&lt;Movie&gt;(){<a name=line.152></a>
    <span class='keyword'>public</span> <span class='keyword'>int</span> compare(Movie aThis, Movie aThat) {<a name=line.153></a>
      <span class='keyword'>if</span> ( aThis == aThat ) <span class='keyword'>return</span> EQUAL;<a name=line.154></a>
<a name=line.155></a>
      <span class='keyword'>int</span> comparison = DESCENDING * comparePossiblyNull(aThis.fRating, aThat.fRating);<a name=line.156></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.157></a>
<a name=line.158></a>
      comparison = DESCENDING * comparePossiblyNull(aThis.fDateViewed, aThat.fDateViewed);<a name=line.159></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.160></a>
      <a name=line.161></a>
      comparison = aThis.fTitle.compareTo(aThat.fTitle);<a name=line.162></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.163></a>
      <a name=line.164></a>
      comparison = comparePossiblyNull(aThis.fComment, aThat.fComment);<a name=line.165></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.166></a>
      <a name=line.167></a>
      <span class='keyword'>return</span> EQUAL;<a name=line.168></a>
    };<a name=line.169></a>
  };<a name=line.170></a>
  <a name=line.171></a>
  <span class='comment'>/** Sort by Comment. */</span><a name=line.172></a>
  <span class='keyword'>static</span> Comparator&lt;Movie&gt; COMMENT_SORT = <span class='keyword'>new</span> Comparator&lt;Movie&gt;(){<a name=line.173></a>
    <span class='keyword'>public</span> <span class='keyword'>int</span> compare(Movie aThis, Movie aThat) {<a name=line.174></a>
      <span class='keyword'>if</span> ( aThis == aThat ) <span class='keyword'>return</span> EQUAL;<a name=line.175></a>
<a name=line.176></a>
      <span class='keyword'>int</span> comparison = comparePossiblyNull(aThis.fComment, aThat.fComment);<a name=line.177></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.178></a>
      <a name=line.179></a>
      comparison = aThis.fTitle.compareTo(aThat.fTitle);<a name=line.180></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.181></a>
      <a name=line.182></a>
      comparison = comparePossiblyNull(aThis.fRating, aThat.fRating);<a name=line.183></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.184></a>
<a name=line.185></a>
      comparison = DESCENDING * comparePossiblyNull(aThis.fDateViewed, aThat.fDateViewed);<a name=line.186></a>
      <span class='keyword'>if</span> ( comparison != EQUAL ) <span class='keyword'>return</span> comparison;<a name=line.187></a>
      <a name=line.188></a>
      <span class='keyword'>return</span> EQUAL;<a name=line.189></a>
    };<a name=line.190></a>
  };<a name=line.191></a>
  <a name=line.192></a>
  <span class='comment'>// PRIVATE //<a name=line.193></a>
</span>  <span class='keyword'>private</span> String fId;<a name=line.194></a>
  <span class='keyword'>private</span> <span class='keyword'>final</span> String fTitle;<a name=line.195></a>
  <span class='keyword'>private</span> <span class='keyword'>final</span> Date fDateViewed;<a name=line.196></a>
  <span class='keyword'>private</span> <span class='keyword'>final</span> BigDecimal fRating;<a name=line.197></a>
  <span class='keyword'>private</span> <span class='keyword'>final</span> String fComment;<a name=line.198></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> BigDecimal TEN = <span class='keyword'>new</span> BigDecimal(<span class='literal'>"10.0"</span>);<a name=line.199></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>int</span> EQUAL = <span class='literal'>0</span>;<a name=line.200></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>int</span> DESCENDING = -<span class='literal'>1</span>;<a name=line.201></a>
  <a name=line.202></a>
  <span class='keyword'>private</span> <span class='keyword'>void</span> validateState() <span class='keyword'>throws</span> InvalidInputException {<a name=line.203></a>
    InvalidInputException ex = <span class='keyword'>new</span> InvalidInputException();<a name=line.204></a>
    <a name=line.205></a>
    <span class='keyword'>if</span>( ! Util.textHasContent(fTitle) ) {<a name=line.206></a>
      ex.add(<span class='literal'>"Title must have content"</span>);<a name=line.207></a>
    }<a name=line.208></a>
    <span class='keyword'>if</span> ( fRating != <span class='keyword'>null</span> ){<a name=line.209></a>
      <span class='keyword'>if</span> ( fRating.compareTo(BigDecimal.ZERO) &lt; <span class='literal'>0</span> ) {<a name=line.210></a>
        ex.add(<span class='literal'>"Rating cannot be less than 0."</span>);<a name=line.211></a>
      }<a name=line.212></a>
      <span class='keyword'>if</span> ( fRating.compareTo(TEN) &gt; <span class='literal'>0</span> ) {<a name=line.213></a>
        ex.add(<span class='literal'>"Rating cannot be greater than 10."</span>);<a name=line.214></a>
      }<a name=line.215></a>
    }<a name=line.216></a>
    <span class='keyword'>if</span> ( ex.hasErrors() ) {<a name=line.217></a>
      <span class='keyword'>throw</span> ex;<a name=line.218></a>
    }<a name=line.219></a>
  }<a name=line.220></a>
  <a name=line.221></a>
  <span class='keyword'>private</span> <span class='keyword'>boolean</span> areEqual(Object aThis, Object aThat){<a name=line.222></a>
    <span class='keyword'>return</span> aThis == <span class='keyword'>null</span> ? aThat == <span class='keyword'>null</span> : aThis.equals(aThat);<a name=line.223></a>
  }<a name=line.224></a>
  <a name=line.225></a>
  <span class='keyword'>private</span> <span class='keyword'>int</span> addHash(<span class='keyword'>int</span> aHash, Object aField){<a name=line.226></a>
    <span class='keyword'>int</span> result = <span class='literal'>37</span>*aHash;<a name=line.227></a>
    <span class='keyword'>if</span> (aField != <span class='keyword'>null</span>){<a name=line.228></a>
      result = result + aField.hashCode();<a name=line.229></a>
    }<a name=line.230></a>
    <span class='keyword'>return</span> result;<a name=line.231></a>
  }<a name=line.232></a>
  <a name=line.233></a>
  <span class='comment'>/** Utility method.  */</span><a name=line.234></a>
  <span class='keyword'>private</span> <span class='keyword'>static</span> &lt;T <span class='keyword'>extends</span> Comparable&gt; <span class='keyword'>int</span> comparePossiblyNull(T aThis, T aThat){<a name=line.235></a>
    <span class='keyword'>int</span> result = EQUAL;<a name=line.236></a>
    <span class='keyword'>int</span> BEFORE = -<span class='literal'>1</span>;<a name=line.237></a>
    <span class='keyword'>int</span> AFTER = <span class='literal'>1</span>;<a name=line.238></a>
    <a name=line.239></a>
    <span class='keyword'>if</span>(aThis != <span class='keyword'>null</span> &amp;&amp; aThat != <span class='keyword'>null</span>){ <a name=line.240></a>
      result = aThis.compareTo(aThat);<a name=line.241></a>
    }<a name=line.242></a>
    <span class='keyword'>else</span> {<a name=line.243></a>
      <span class='comment'>//at least one reference is null - special handling<a name=line.244></a>
</span>      <span class='keyword'>if</span>(aThis == <span class='keyword'>null</span> &amp;&amp; aThat == <span class='keyword'>null</span>) {<a name=line.245></a>
        <span class='comment'>//do nothing - they are not distinct <a name=line.246></a>
</span>      }<a name=line.247></a>
      <span class='keyword'>else</span> <span class='keyword'>if</span>(aThis == <span class='keyword'>null</span> &amp;&amp; aThat != <span class='keyword'>null</span>) {<a name=line.248></a>
        result = BEFORE;<a name=line.249></a>
      }<a name=line.250></a>
      <span class='keyword'>else</span> <span class='keyword'>if</span>( aThis != <span class='keyword'>null</span> &amp;&amp; aThat == <span class='keyword'>null</span>) {<a name=line.251></a>
        result = AFTER;<a name=line.252></a>
      }<a name=line.253></a>
    }<a name=line.254></a>
    <span class='keyword'>return</span> result;<a name=line.255></a>
  }<a name=line.256></a>
} <a name=line.257></a>
</PRE><a name=line.258></a>
 </BODY><a name=line.259></a>
<HTML><a name=line.260></a>
